#!/bin/bash
###############################################################################
#       脚本名称: utils_yq.sh
#       功能描述: YAML数据格式处理工具模块，提供纯粹的YAML数据操作功能.
#       作者: 林再来
#       日期: 2025-10-15
#       版本: 1.0
#       功能:
#           1、YAML文件读取、写入、删除操作
#           2、YAML文件格式验证和转换
#           3、YAML文件合并和格式化功能
#           4、YAML与JSON互转功能
#           5、支持yq原生查询功能
#       依赖(插件/命令):
#           1、yq命令行工具
#           2、curl(用于下载yq工具，可选)
###############################################################################

# 防止重复加载
if [[ -n "${utils_yq_sh_loaded:-}" ]]; then
    return 0
fi
readonly utils_yq_sh_loaded=1

# 注意：不使用严格模式，避免影响交互式 shell

# 获取脚本目录和项目根目录（如果尚未定义）
if [[ -z "${script_dir:-}" ]]; then
    script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    readonly script_dir
fi

if [[ -z "${project_root:-}" ]]; then
    project_root="$(cd "${script_dir}/../.." && pwd)"
    readonly project_root
fi

###############################################################################
# 全局常量
###############################################################################

readonly UTILS_YQ_DEFAULT_VERSION="v4.35.1"
readonly UTILS_YQ_PACKAGES_DIR="$project_root/packages/yq"

###############################################################################
# 全局变量
###############################################################################

utils_yq_binary="${utils_yq_binary:-}"
utils_yq_version="${utils_yq_version:-$UTILS_YQ_DEFAULT_VERSION}"

###############################################################################
# 工具初始化
###############################################################################

# 函数: 初始化yq工具
# 参数: 无
# 返回: 0-成功, 1-失败
# 说明: 自动检测系统yq或使用项目内置yq
function utils_yq_init() {
    log_debug "utils_yq" "初始化yq工具"
    
    # 如果utils_yq_binary已设置，检查是否可用
    if [[ -n "$utils_yq_binary" ]] && command -v "$utils_yq_binary" >/dev/null 2>&1; then
        log_debug "utils_yq" "使用已设置的yq: $utils_yq_binary"
        return 0
    fi
    
    # 检查系统是否已安装yq
    if command -v yq >/dev/null 2>&1; then
        utils_yq_binary="yq"
        log_info "utils_yq" "使用系统yq: $(which yq)"
        return 0
    fi
    
    # 检查项目内置yq
    local arch=""
    case "$(uname -m)" in
        x86_64|amd64) 
            arch="amd64" 
            ;;
        aarch64|arm64) 
            arch="arm64" 
            ;;
        *) 
            log_warn "utils_yq" "未知架构: $(uname -m)，尝试使用amd64"
            arch="amd64" 
            ;;
    esac
    
    local yq_path="$UTILS_YQ_PACKAGES_DIR/$utils_yq_version/$arch/yq_linux_$utils_yq_version"
    
    if [[ -f "$yq_path" ]]; then
        chmod +x "$yq_path" 2>/dev/null || {
            log_error "utils_yq" "错误: 无法设置yq执行权限: $yq_path"
            return 1
        }
        
        if [[ -x "$yq_path" ]]; then
            utils_yq_binary="$yq_path"
            log_info "utils_yq" "使用内置yq: $yq_path"
            return 0
        else
            log_error "utils_yq" "错误: yq文件无执行权限: $yq_path"
            return 1
        fi
    else
        log_error "utils_yq" "错误: 未找到内置yq: $yq_path"
    fi
    
    log_error "utils_yq" "错误: 未找到可用的yq工具"
    return 1
}

###############################################################################
# 基础数据操作
###############################################################################

# 函数: 验证YAML文件格式
# 参数: $1 - YAML文件路径
# 返回: 0-格式正确, 1-格式错误
function utils_yq_validate() {
    local yaml_file="$1"
    
    if [[ -z "$yaml_file" ]]; then
        log_error "utils_yq" "错误: 缺少参数: YAML文件路径"
        return 1
    fi
    
    if [[ ! -f "$yaml_file" ]]; then
        log_error "utils_yq" "错误: YAML文件不存在: $yaml_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    log_debug "utils_yq" "验证YAML文件格式: $yaml_file"
    
    if "$utils_yq_binary" eval '.' "$yaml_file" >/dev/null 2>&1; then
        log_debug "utils_yq" "YAML文件格式正确: $yaml_file"
        return 0
    else
        log_error "utils_yq" "错误: YAML文件格式错误: $yaml_file"
        return 1
    fi
}

# 函数: 读取YAML文件中的值
# 参数: $1 - YAML文件路径, $2 - 要读取的键路径
# 返回: 0-成功, 1-失败
# 输出: 读取的值(stdout)
function utils_yq_read() {
    local yaml_file="$1"
    local key="$2"
    
    if [[ -z "$yaml_file" ]] || [[ -z "$key" ]]; then
        log_error "utils_yq" "错误: 缺少参数: YAML文件路径和键"
        return 1
    fi
    
    if [[ ! -f "$yaml_file" ]]; then
        log_error "utils_yq" "错误: YAML文件不存在: $yaml_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    log_debug "utils_yq" "读取YAML值: $yaml_file -> $key"
    
    local result
    if result=$("$utils_yq_binary" eval "$key" "$yaml_file" 2>/dev/null); then
        # 如果结果是 null，返回空字符串
        if [[ "$result" = "null" ]]; then
            echo ""
        else
            echo "$result"
        fi
        return 0
    else
        log_error "utils_yq" "错误: 读取YAML值失败: $yaml_file -> $key"
        return 1
    fi
}

# 函数: 写入值到YAML文件
# 参数: $1 - YAML文件路径, $2 - 要写入的键路径, $3 - 要写入的值, $4 - 值类型(可选: string/number/boolean)
# 返回: 0-成功, 1-失败
function utils_yq_write() {
    local yaml_file="$1"
    local key="$2"
    local value="$3"
    local value_type="${4:-string}"
    
    if [[ -z "$yaml_file" ]] || [[ -z "$key" ]]; then
        log_error "utils_yq" "错误: 缺少参数: YAML文件路径和键"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    # 如果文件不存在，创建空的YAML文件
    if [[ ! -f "$yaml_file" ]]; then
        log_info "utils_yq" "YAML文件不存在，创建新文件: $yaml_file"
        echo "{}" > "$yaml_file"
    fi
    
    log_debug "utils_yq" "写入YAML值: $yaml_file -> $key = $value ($value_type)"
    
    # 根据值类型处理
    local yq_expression
    case "$value_type" in
        "number")
            yq_expression="$key = $value"
            ;;
        "boolean")
            yq_expression="$key = $value"
            ;;
        "string"|*)
            yq_expression="$key = \"$value\""
            ;;
    esac
    
    # 备份原文件
    local backup_file="${yaml_file}.bak.$(date +%s)"
    cp "$yaml_file" "$backup_file" || {
        log_error "utils_yq" "错误: 无法创建备份文件: $backup_file"
        return 1
    }
    
    # 执行写入操作
    if "$utils_yq_binary" eval "$yq_expression" -i "$yaml_file" 2>/dev/null; then
        # 验证结果
        if utils_yq_validate "$yaml_file"; then
            rm -f "$backup_file"
            log_debug "utils_yq" "YAML值写入成功"
            return 0
        else
            log_error "utils_yq" "错误: 写入后YAML文件格式错误，恢复备份"
            mv "$backup_file" "$yaml_file"
            return 1
        fi
    else
        log_error "utils_yq" "错误: YAML值写入失败，恢复备份"
        mv "$backup_file" "$yaml_file"
        return 1
    fi
}

# 函数: 删除YAML文件中的键
# 参数: $1 - YAML文件路径, $2 - 要删除的键路径
# 返回: 0-成功, 1-失败
function utils_yq_delete() {
    local yaml_file="$1"
    local key="$2"
    
    if [[ -z "$yaml_file" ]] || [[ -z "$key" ]]; then
        log_error "utils_yq" "错误: 缺少参数: YAML文件路径和键"
        return 1
    fi
    
    if [[ ! -f "$yaml_file" ]]; then
        log_error "utils_yq" "错误: YAML文件不存在: $yaml_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    log_debug "utils_yq" "删除YAML键: $yaml_file -> $key"
    
    # 备份原文件
    local backup_file="${yaml_file}.bak.$(date +%s)"
    cp "$yaml_file" "$backup_file" || {
        log_error "utils_yq" "错误: 无法创建备份文件: $backup_file"
        return 1
    }
    
    # 执行删除操作
    if "$utils_yq_binary" eval "del($key)" -i "$yaml_file" 2>/dev/null; then
        # 验证结果
        if utils_yq_validate "$yaml_file"; then
            rm -f "$backup_file"
            log_debug "utils_yq" "YAML键删除成功"
            return 0
        else
            log_error "utils_yq" "错误: 删除后YAML文件格式错误，恢复备份"
            mv "$backup_file" "$yaml_file"
            return 1
        fi
    else
        log_error "utils_yq" "错误: YAML键删除失败，恢复备份"
        mv "$backup_file" "$yaml_file"
        return 1
    fi
}

###############################################################################
# 文件操作
###############################################################################

# 函数: 合并YAML文件
# 参数: $1 - 目标文件, $2 - 源文件, $3 - 合并模式(可选: merge/overwrite)
# 返回: 0-成功, 1-失败
# 说明: merge模式递归合并，overwrite模式完全覆盖
function utils_yq_merge() {
    local target_file="$1"
    local source_file="$2"
    local merge_mode="${3:-merge}"
    
    if [[ -z "$target_file" ]] || [[ -z "$source_file" ]]; then
        log_error "utils_yq" "错误: 缺少参数: 目标文件和源文件路径"
        return 1
    fi
    
    if [[ ! -f "$source_file" ]]; then
        log_error "utils_yq" "错误: 源文件不存在: $source_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    # 如果目标文件不存在，直接复制源文件
    if [[ ! -f "$target_file" ]]; then
        log_info "utils_yq" "目标文件不存在，复制源文件: $target_file"
        cp "$source_file" "$target_file"
        return $?
    fi
    
    log_debug "utils_yq" "合并YAML文件: $source_file -> $target_file (模式: $merge_mode)"
    
    # 备份目标文件
    local backup_file="${target_file}.bak.$(date +%s)"
    cp "$target_file" "$backup_file" || {
        log_error "utils_yq" "错误: 无法创建备份文件: $backup_file"
        return 1
    }
    
    # 根据合并模式执行操作
    local merge_expression
    case "$merge_mode" in
        "overwrite")
            merge_expression=". * load(\"$source_file\")"
            ;;
        "merge"|*)
            merge_expression=". *+ load(\"$source_file\")"
            ;;
    esac
    
    # 执行合并操作
    if "$utils_yq_binary" eval "$merge_expression" -i "$target_file" 2>/dev/null; then
        # 验证结果
        if utils_yq_validate "$target_file"; then
            rm -f "$backup_file"
            log_debug "utils_yq" "YAML文件合并成功"
            return 0
        else
            log_error "utils_yq" "错误: 合并后YAML文件格式错误，恢复备份"
            mv "$backup_file" "$target_file"
            return 1
        fi
    else
        log_error "utils_yq" "错误: YAML文件合并失败，恢复备份"
        mv "$backup_file" "$target_file"
        return 1
    fi
}

# 函数: 列出YAML文件中的所有键
# 参数: $1 - YAML文件路径, $2 - 父键路径(可选)
# 返回: 0-成功, 1-失败
# 输出: 键列表(stdout)
function utils_yq_list_keys() {
    local yaml_file="$1"
    local parent_key="${2:-}"
    
    if [[ -z "$yaml_file" ]]; then
        log_error "utils_yq" "错误: 缺少参数: YAML文件路径"
        return 1
    fi
    
    if [[ ! -f "$yaml_file" ]]; then
        log_error "utils_yq" "错误: YAML文件不存在: $yaml_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    log_debug "utils_yq" "列出YAML键: $yaml_file"
    
    local expression
    if [[ -n "$parent_key" ]]; then
        expression="$parent_key | keys | .[]"
    else
        expression="keys | .[]"
    fi
    
    if "$utils_yq_binary" eval "$expression" "$yaml_file" 2>/dev/null; then
        return 0
    else
        log_error "utils_yq" "错误: 列出YAML键失败: $yaml_file"
        return 1
    fi
}

# 函数: 格式化YAML文件
# 参数: $1 - YAML文件路径, $2 - 缩进空格数(可选，默认2)
# 返回: 0-成功, 1-失败
function utils_yq_format() {
    local yaml_file="$1"
    local indent="${2:-2}"
    
    if [[ -z "$yaml_file" ]]; then
        log_error "utils_yq" "错误: 缺少参数: YAML文件路径"
        return 1
    fi
    
    if [[ ! -f "$yaml_file" ]]; then
        log_error "utils_yq" "错误: YAML文件不存在: $yaml_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    log_debug "utils_yq" "格式化YAML文件: $yaml_file (缩进: $indent)"
    
    # 备份原文件
    local backup_file="${yaml_file}.bak.$(date +%s)"
    cp "$yaml_file" "$backup_file" || {
        log_error "utils_yq" "错误: 无法创建备份文件: $backup_file"
        return 1
    }
    
    # 执行格式化
    if "$utils_yq_binary" eval '.' "$yaml_file" --indent "$indent" > "${yaml_file}.tmp" 2>/dev/null; then
        if mv "${yaml_file}.tmp" "$yaml_file"; then
            rm -f "$backup_file"
            log_debug "utils_yq" "YAML文件格式化成功"
            return 0
        else
            log_error "utils_yq" "错误: 无法保存格式化结果，恢复备份"
            mv "$backup_file" "$yaml_file"
            rm -f "${yaml_file}.tmp"
            return 1
        fi
    else
        log_error "utils_yq" "错误: YAML文件格式化失败，恢复备份"
        mv "$backup_file" "$yaml_file"
        rm -f "${yaml_file}.tmp"
        return 1
    fi
}

###############################################################################
# 格式转换
###############################################################################

# 函数: 将YAML转换为JSON
# 参数: $1 - YAML文件路径, $2 - JSON输出文件路径(可选，为空则输出到stdout)
# 返回: 0-成功, 1-失败
# 输出: JSON内容(stdout或文件)
function utils_yq_to_json() {
    local yaml_file="$1"
    local json_file="${2:-}"
    
    if [[ -z "$yaml_file" ]]; then
        log_error "utils_yq" "错误: 缺少参数: YAML文件路径"
        return 1
    fi
    
    if [[ ! -f "$yaml_file" ]]; then
        log_error "utils_yq" "错误: YAML文件不存在: $yaml_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    log_debug "utils_yq" "转换YAML到JSON: $yaml_file"
    
    if [[ -n "$json_file" ]]; then
        # 输出到文件
        if "$utils_yq_binary" eval '.' "$yaml_file" -o json > "$json_file" 2>/dev/null; then
            log_debug "utils_yq" "YAML转JSON成功: $json_file"
            return 0
        else
            log_error "utils_yq" "错误: YAML转JSON失败"
            return 1
        fi
    else
        # 输出到标准输出
        "$utils_yq_binary" eval '.' "$yaml_file" -o json 2>/dev/null
        return $?
    fi
}

# 函数: 从JSON转换为YAML
# 参数: $1 - JSON文件路径, $2 - YAML输出文件路径(可选，为空则输出到stdout)
# 返回: 0-成功, 1-失败
# 输出: YAML内容(stdout或文件)
function utils_yq_from_json() {
    local json_file="$1"
    local yaml_file="${2:-}"
    
    if [[ -z "$json_file" ]]; then
        log_error "utils_yq" "错误: 缺少参数: JSON文件路径"
        return 1
    fi
    
    if [[ ! -f "$json_file" ]]; then
        log_error "utils_yq" "错误: JSON文件不存在: $json_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    log_debug "utils_yq" "转换JSON到YAML: $json_file"
    
    if [[ -n "$yaml_file" ]]; then
        # 输出到文件
        if "$utils_yq_binary" eval '.' "$json_file" -P > "$yaml_file" 2>/dev/null; then
            log_debug "utils_yq" "JSON转YAML成功: $yaml_file"
            return 0
        else
            log_error "utils_yq" "错误: JSON转YAML失败"
            return 1
        fi
    else
        # 输出到标准输出
        "$utils_yq_binary" eval '.' "$json_file" -P 2>/dev/null
        return $?
    fi
}

###############################################################################
# 高级查询
###############################################################################

# 函数: 执行yq查询表达式
# 参数: $1 - YAML文件路径, $2 - yq查询表达式
# 返回: 0-成功, 1-失败
# 输出: 查询结果(stdout)
# 说明: 支持yq的所有原生查询功能
function utils_yq_query() {
    local yaml_file="$1"
    local query="$2"
    
    if [[ -z "$yaml_file" ]] || [[ -z "$query" ]]; then
        log_error "utils_yq" "错误: 缺少参数: YAML文件路径和查询表达式"
        return 1
    fi
    
    if [[ ! -f "$yaml_file" ]]; then
        log_error "utils_yq" "错误: YAML文件不存在: $yaml_file"
        return 1
    fi
    
    # 确保yq工具可用
    if ! utils_yq_init; then
        return 1
    fi
    
    log_debug "utils_yq" "执行YAML查询: $yaml_file -> $query"
    
    # 执行查询
    if "$utils_yq_binary" eval "$query" "$yaml_file" 2>/dev/null; then
        return 0
    else
        log_error "utils_yq" "错误: YAML查询失败: $query"
        return 1
    fi
}

###############################################################################
# 帮助函数
###############################################################################

# 函数: 显示帮助信息
function utils_yq_help() {
    cat << 'EOF'
YAML数据处理工具 (utils_yq.sh) - 使用说明

功能函数：
  utils_yq_init                              - 初始化yq工具
  utils_yq_validate <yaml_file>              - 验证YAML文件格式
  utils_yq_read <yaml_file> <key>            - 读取YAML值
  utils_yq_write <yaml_file> <key> <value> [type] - 写入YAML值
  utils_yq_delete <yaml_file> <key>          - 删除YAML键
  utils_yq_merge <target> <source> [mode]    - 合并YAML文件
  utils_yq_list_keys <yaml_file> [parent]    - 列出所有键
  utils_yq_format <yaml_file> [indent]       - 格式化YAML文件
  utils_yq_to_json <yaml_file> [json_file]   - YAML转换为JSON
  utils_yq_from_json <json_file> [yaml_file] - JSON转换为YAML
  utils_yq_query <yaml_file> <expression>    - 执行yq查询

参数说明：
  yaml_file  - YAML文件路径
  key        - 键路径，如：.app.name, .database.port
  value      - 要设置的值
  type       - 值类型：string(默认)|number|boolean
  mode       - 合并模式：merge(默认)|overwrite
  indent     - 缩进空格数（默认：2）
  expression - yq查询表达式

使用示例：
  # 基本操作
  utils_yq_read config.yaml ".app.name"
  utils_yq_write config.yaml ".app.port" "8080" "number"
  utils_yq_delete config.yaml ".database.password"
  
  # 文件操作
  utils_yq_merge target.yaml source.yaml "merge"
  utils_yq_format config.yaml 4
  
  # 格式转换
  utils_yq_to_json config.yaml config.json
  utils_yq_from_json data.json data.yaml
  
  # 高级查询
  utils_yq_query config.yaml '.servers[] | select(.active == true)'
  utils_yq_query config.yaml '.users | map(select(.age > 18))'

注意事项：
  1. 本模块为纯数据处理工具，不包含业务逻辑
  2. 配置管理特定功能请使用 config/yaml.sh
  3. 所有写入操作都会自动创建备份文件
  4. 支持yq v4.x的所有查询语法

EOF
}

###############################################################################
# 简单日志函数（如果外部日志模块不可用）
###############################################################################

if ! command -v log_debug >/dev/null 2>&1; then
    function log_debug() { 
        local module="${1:-}"
        shift
        echo "[DEBUG] [$module] $*" >&2
    }
fi

if ! command -v log_info >/dev/null 2>&1; then
    function log_info() {
        local module="${1:-}"
        shift
        echo "[INFO]  [$module] $*" >&2
    }
fi

if ! command -v log_warn >/dev/null 2>&1; then
    function log_warn() {
        local module="${1:-}"
        shift
        echo "[WARN]  [$module] $*" >&2
    }
fi

if ! command -v log_error >/dev/null 2>&1; then
    function log_error() {
        local module="${1:-}"
        shift
        echo "[ERROR] [$module] $*" >&2
    }
fi

